diff --git a/web/calendar/calendar.css b/web/calendar/calendar.css index 015e4f5ac..d1eecb73a 100644 --- a/web/calendar/calendar.css +++ b/web/calendar/calendar.css @@ -1,191 +1,178 @@ a.filtersButton { font-family: "Open Sans", sans-serif; font-weight: 600; float: left; color: white; background-color: rgba(68,68,85,0.67); padding: 8px 18px; border-radius: 18px; margin: 0 15px 5px 15px; font-size: 16px; cursor: pointer; text-transform: uppercase; position: absolute; } a.filtersButton > svg { padding-right: 5px; } div.content { min-height: 100%; padding-top: 10px; position: relative; } h2.nav { font-family: 'Anaheim', sans-serif; text-align: center; margin: 8px auto 12px auto; width: 250px; color: #333333; display: flex; justify-content: space-between; } h2.nav > a.monthLink { color: #036AFF; } h2.nav > div.monthName { display: inline-block; width: 200px; } table.calendar { table-layout: fixed; padding: 10px; width: 100%; } table.calendar > thead > tr > th { padding-bottom: 6px; color: #888888; font-weight: normal; text-transform: uppercase; font-size: 14px; } textarea.entryText { - background: none; - width: 100%; - outline: none; - border: none; - resize: none; - overflow: auto; - overflow-y: hidden; - padding-right: 15px; - box-sizing: border-box; - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", - "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", - "Helvetica Neue", sans-serif; - font-size: 11px; } table.calendar td.day { border: 1px solid transparent; background-color: white; position: relative; vertical-align: top; line-height: 0; cursor: text; height: 120px; } td.day > div.entryContainer { overflow: auto; height: 120px; } td.day > div.focusedEntryContainer { height: 100px !important; } div.entryContainer > div.entryContainerSpacer { height: 13px; } div.entry { padding: 5px; box-shadow: inset 0px 0px 0px 2px white; border-radius: 5px; position: relative; } div.darkEntry { color: white; } div.darkEntry textarea.entryText { color: white; } div.darkEntry div.actionLinks > a { color: lightgray; } div.darkEntry div.actionLinks > a:hover { color: white; } span.entryLoading { position: absolute; top: 5px; right: 5px; width: 12px; } span.entryError { position: absolute; top: 11px; right: 5px; font-size: 16px; width: 6px; color: red !important; } div.actionLinks { line-height: 90%; margin-top: 3px; font-size: 16px; } div.dayActionLinks { padding-left: 3px; margin-bottom: 1px; height: 16px; } div.actionLinks a + a { margin-left: 6px; } span.actionLinksText { font-family: 'Open Sans', sans-serif; font-weight: 600; font-size: 11px; position: relative; bottom: 1px; padding-left: 5px; } span.rightActionLinks { float: right; text-overflow: ellipsis; white-space: nowrap; max-width: 100%; display: inline-block; overflow: hidden; } div.actionLinks svg { fill: gray; width: 10px; height: 10px; } div.actionLinks svg.history { position: relative; top: 1px; } div.darkEntry div.actionLinks svg { fill: lightgray; } div.actionLinks a:hover svg { fill: black; } div.darkEntry div.actionLinks a:hover svg { fill: white; } div.focusedEntry { z-index: 3 !important; } div.actionLinks a { color: gray; } div.actionLinks a:hover { color: black; } td.day > h2 { position: absolute; bottom: 2px; right: 3px; color: #999999; font-size: 32px; line-height: 32px; z-index: 1; font-family: 'Anaheim', sans-serif; } td.currentDay > h2 { color: #FF944D; } div.clear { clear: both; } diff --git a/web/calendar/calendar.react.js b/web/calendar/calendar.react.js index 94a93e8a4..990f35b7d 100644 --- a/web/calendar/calendar.react.js +++ b/web/calendar/calendar.react.js @@ -1,281 +1,271 @@ // @flow import { faFilter } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import dateFormat from 'dateformat'; import invariant from 'invariant'; import PropTypes from 'prop-types'; import * as React from 'react'; import { updateCalendarQueryActionTypes, updateCalendarQuery, } from 'lib/actions/entry-actions'; import { currentDaysToEntries } from 'lib/selectors/thread-selectors'; import { entryInfoPropType, type EntryInfo, type CalendarQuery, type CalendarQueryUpdateResult, type CalendarQueryUpdateStartingPayload, } from 'lib/types/entry-types'; import type { DispatchActionPromise } from 'lib/utils/action-utils'; import { getDate, dateString, startDateForYearAndMonth, endDateForYearAndMonth, } from 'lib/utils/date-utils'; import { connect } from 'lib/utils/redux-utils'; import { type AppState, type NavInfo, navInfoPropType, } from '../redux/redux-setup'; import { yearAssertingSelector, monthAssertingSelector, webCalendarQuery, } from '../selectors/nav-selectors'; import { canonicalURLFromReduxState } from '../url-utils'; import css from './calendar.css'; import Day from './day.react'; import FilterPanel from './filter-panel.react'; type Props = { setModal: (modal: ?React.Node) => void, url: string, // Redux state year: number, month: number, // 1-indexed daysToEntries: { [dayString: string]: EntryInfo[] }, navInfo: NavInfo, currentCalendarQuery: () => CalendarQuery, // Redux dispatch functions dispatchActionPromise: DispatchActionPromise, // async functions that hit server APIs updateCalendarQuery: ( calendarQuery: CalendarQuery, reduxAlreadyUpdated?: boolean, ) => Promise, }; type State = {| filterPanelOpen: boolean, |}; class Calendar extends React.PureComponent { static propTypes = { setModal: PropTypes.func.isRequired, url: PropTypes.string.isRequired, year: PropTypes.number.isRequired, month: PropTypes.number.isRequired, daysToEntries: PropTypes.objectOf(PropTypes.arrayOf(entryInfoPropType)) .isRequired, navInfo: navInfoPropType.isRequired, currentCalendarQuery: PropTypes.func.isRequired, dispatchActionPromise: PropTypes.func.isRequired, updateCalendarQuery: PropTypes.func.isRequired, }; state: State = { filterPanelOpen: false, }; getDate( dayOfMonth: number, monthInput: ?number = undefined, yearInput: ?number = undefined, ) { return getDate( yearInput ? yearInput : this.props.year, monthInput ? monthInput : this.props.month, dayOfMonth, ); } prevMonthDates() { const { year, month } = this.props; const lastMonthDate = getDate(year, month - 1, 1); const prevYear = lastMonthDate.getFullYear(); const prevMonth = lastMonthDate.getMonth() + 1; return { startDate: startDateForYearAndMonth(prevYear, prevMonth), endDate: endDateForYearAndMonth(prevYear, prevMonth), }; } nextMonthDates() { const { year, month } = this.props; const nextMonthDate = getDate(year, month + 1, 1); const nextYear = nextMonthDate.getFullYear(); const nextMonth = nextMonthDate.getMonth() + 1; return { startDate: startDateForYearAndMonth(nextYear, nextMonth), endDate: endDateForYearAndMonth(nextYear, nextMonth), }; } render() { const { year, month } = this.props; const monthName = dateFormat(getDate(year, month, 1), 'mmmm'); const prevURL = canonicalURLFromReduxState( { ...this.props.navInfo, ...this.prevMonthDates() }, this.props.url, ); const nextURL = canonicalURLFromReduxState( { ...this.props.navInfo, ...this.nextMonthDates() }, this.props.url, ); const lastDayOfMonth = this.getDate(0, this.props.month + 1); const totalDaysInMonth = lastDayOfMonth.getDate(); const firstDayToPrint = 1 - this.getDate(1).getDay(); const lastDayToPrint = totalDaysInMonth + 6 - lastDayOfMonth.getDay(); const rows = []; let columns = []; let week = 1; let tabIndex = 1; for ( let curDayOfMonth = firstDayToPrint; curDayOfMonth <= lastDayToPrint; curDayOfMonth++ ) { if (curDayOfMonth < 1 || curDayOfMonth > totalDaysInMonth) { columns.push(); } else { const dayString = dateString( this.props.year, this.props.month, curDayOfMonth, ); const entries = this.props.daysToEntries[dayString]; invariant( entries, 'the currentDaysToEntries selector should make sure all dayStrings ' + `in the current range have entries, but ${dayString} did not`, ); columns.push( , ); tabIndex += entries.length; } if (columns.length === 7) { rows.push({columns}); columns = []; } } let filterPanel = null; let calendarContentStyle = null; let filterButtonStyle = null; if (this.state.filterPanelOpen) { filterPanel = ; calendarContentStyle = { marginLeft: '300px' }; filterButtonStyle = { backgroundColor: 'rgba(0,0,0,0.67)' }; } return (
{filterPanel}
Filters

<
{' '} {monthName} {year}{' '}
>

- - - - - - - - - - - + {rows}
SundayMondayTuesdayWednesdayThursdayFridaySaturday
); } toggleFilters = (event: SyntheticEvent) => { event.preventDefault(); this.setState({ filterPanelOpen: !this.state.filterPanelOpen }); }; onClickPrevURL = (event: SyntheticEvent) => { event.preventDefault(); const currentCalendarQuery = this.props.currentCalendarQuery(); const newCalendarQuery = { ...currentCalendarQuery, ...this.prevMonthDates(), }; this.props.dispatchActionPromise( updateCalendarQueryActionTypes, this.props.updateCalendarQuery(newCalendarQuery, true), undefined, ({ calendarQuery: newCalendarQuery }: CalendarQueryUpdateStartingPayload), ); }; onClickNextURL = (event: SyntheticEvent) => { event.preventDefault(); const currentCalendarQuery = this.props.currentCalendarQuery(); const newCalendarQuery = { ...currentCalendarQuery, ...this.nextMonthDates(), }; this.props.dispatchActionPromise( updateCalendarQueryActionTypes, this.props.updateCalendarQuery(newCalendarQuery, true), undefined, ({ calendarQuery: newCalendarQuery }: CalendarQueryUpdateStartingPayload), ); }; } export default connect( (state: AppState) => ({ year: yearAssertingSelector(state), month: monthAssertingSelector(state), daysToEntries: currentDaysToEntries(state), navInfo: state.navInfo, currentCalendarQuery: webCalendarQuery(state), }), { updateCalendarQuery }, )(Calendar);